home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
System Booster
/
System Booster.iso
/
Archives
/
GNU
/
GNUPLOTsrc.lha
/
datafile.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-01-22
|
34KB
|
1,327 lines
#ifndef lint
static char *RCSid = "$Id: datafile.c,v 1.17 1995/12/12 22:10:46 drd Exp $";
#endif
/*
* this file provides the functions to handle data-file reading..
* takes care of all the pipe / stdin / index / using worries
*/
/*{{{ notes*/
/* couldn't decide how to implement 'thru' only for 2d and 'index'
* for only 3d, so I did them for both - I can see a use for
* index in 2d, especially for fit.
*
* I keep thru for backwards compatibility, and extend it to allow
* more natural plot 'data' thru f(y) - I (personally) prefer
* my syntax, but then I'm biased...
*
* - because I needed it, I have added a range of indexes...
* (s)plot 'data' [index i[:j]]
*
* also every a:b:c:d:e:f - plot every a'th point from c to e,
* in every b lines from d to f
* ie for (line=d; line<=f; line+=b)
* for (point=c; point >=e; point+=a)
*
*
* I dont like mixing this with the time series hack... I am
* very into modular code, so I would prefer to not have to
* have _anything_ to do with time series... for example,
* we just look at columns in file, and that is independent
* of 2d/3d. I really dont want to have to pass a flag to
* this is plot or splot.
*
* use a global array df_timecol[] - cleared by df_open, then
* columns needing time set by client.
*
* Now that df_2dbinary() and df_3dbinary() are here, I am seriously
* tempted to move get_data() and get_3ddata() in here too
*
* public variables declared in this file.
* int df_no_use_specs - number of columns specified with 'using'
* int df_line_number - for error reporting
* int df_datum - increases with each data point
* TBOOLEAN df_binary - it's a binary file
* [ might change this to return value from df_open() ]
* int df_eof - end of file
* int df_timecol[] - client controls which cols read as time
*
* functions
* int df_open(int max_using)
* parses thru / index / using on command line
* max_using is max no of 'using' columns allowed
* returns number of 'using' cols specified, or -1 on error (?)
*
* int df_readline(double vector[], int max)
* reads a line, does all the 'index' and 'using' manipulation
* deposits values into vector[]
* returns
* number of columns parsed [0=not blank line, but no valid data],
* DF_EOF for EOF
* DF_UNDEFINED - undefined result during eval of extended using spec
* DF_FIRST_BLANK for first consecutive blank line
* DF_SECOND_BLANK for second consecutive blank line
* will return FIRST before SECOND
*
* if a using spec was given, lines not fulfilling spec are ignored.
* we will always return exactly the number of items specified
*
* if no spec given, we return number of consecutive columns we parsed.
*
* if we are processing indexes, seperated by 'n' blank lines,
* we will return n-1 blank lines before noticing the index change
*
* void df_close()
* closes a currently open file.
*
* void f_dollars(x)
* void f_column() actions for expressions using $i, column(j), etc
* void f_valid()
*
*
* line parsing slightly differently from previous versions of gnuplot...
* given a line containing fewer columns than asked for, gnuplot used to make
* up values... I say that if I have explicitly said 'using 1:2:3', then if
* column 3 doesn't exist, I dont want this point...
* a column number of 0 means generate a value... as before, this value
* is useful in 2d, but probably meaningless in 3d ???
*
* 20/5/95 : accept 1.23d4 in place of e (but not in scanf string)
* : autoextend data line buffer and MAX_COLS
*
*/
/*}}}*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <time.h>
#include <assert.h>
#include "plot.h"
#include "fnproto.h" /* check prototypes against our defns */
#include "binary.h"
#include "setshow.h"
/* if you change this, change the scanf in readline */
#define NCOL 7 /* max using specs */
/*{{{ static fns*/
static int get_time_cols __P((char *fmt));
static void mod_def_usespec __P((int specno, int jump));
static int check_missing __P((char *s));
/*}}}*/
/*{{{ variables*/
struct use_spec_s { int column; struct at_type *at; };
/* public variables client might access */
int df_no_use_specs; /* how many using columns were specified */
int df_line_number;
int df_datum; /* suggested x value if none given */
TBOOLEAN df_binary = FALSE; /* this is a binary file */
int df_eof=0;
int df_timecol[NCOL];
/* private variables */
/* in order to allow arbitrary data line length, we need to use the heap
* might consider free-ing it in df_close, especially for small systems
*/
static char *line=NULL;
static int max_line_len=0;
static FILE *data_fp=NULL;
static TBOOLEAN pipe_open = FALSE;
static TBOOLEAN mixed_data_fp = FALSE;
#ifndef MAXINT /* should there be one already defined ? */
#ifdef INT_MAX /* in limits.h ? */
#define MAXINT INT_MAX
#else
#define MAXINT ((~0)>>1)
#endif
#endif
/* stuff for implementing index */
static int blank_count=0; /* how many blank lines recently */
static int df_lower_index=0; /* first mesh required */
static int df_upper_index=MAXINT;
static int df_index_step=1; /* 'every' for indices */
static int df_current_index; /* current mesh */
/* stuff for every point:line */
static int everypoint=1;
static int firstpoint=0;
static int lastpoint=MAXINT;
static int everyline=1;
static int firstline=0;
static int lastline=MAXINT;
static int point_count=-1; /* point counter - preincrement and test 0 */
static int line_count=0; /* line counter */
/* parsing stuff */
static struct use_spec_s use_spec[NCOL];
static char df_format[MAX_LINE_LEN+1];
/* rather than three arrays which all grow dynamically, make one
* dynamic array of this structure
*/
typedef struct df_column_struct {
double datum;
int good; /* 0=bad number (might still be a time), 1=good, -1=missing */
char *position;
} df_column_struct;
static df_column_struct *df_column=NULL; /* we'll allocate space as needed */
static int df_max_cols=0; /* space allocated */
static int df_no_cols; /* cols read */
/* external variables we need */
extern int c_token, num_tokens;
extern char timefmt[]; /* I would rather not need this, but ... */
/* columns needing timefmt are passed in df_timecol[] after df_open */
/* jev -- for passing data thru user-defined function */
extern struct udft_entry ydata_func;
extern struct udft_entry *dummy_func;
extern char dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
extern char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
extern double min_array[], max_array[];
/*}}}*/
/*{{{ replacement sscanf for purec*/
#ifdef __PUREC__
/*
* a substitute for PureC's buggy sscanf.
* this uses the normal sscanf and fixes the following bugs:
* - whitespace in format matches whitespace in string, but doesn't
* require any. ( "%f , %f" scans "1,2" correctly )
* - the ignore value feature works (*). this created an address error
* in PureC.
*/
#include <stdarg.h>
#include <string.h>
int purec_sscanf( const char *string, const char *format, ... )
{
va_list args;
int cnt=0;
char onefmt[256];
char buffer[256];
const char *f=format;
const char *s=string;
char *f2;
char ch;
int ignore;
void *p;
int *ip;
int pos;
va_start(args,format);
while( *f && *s ) {
ch=*f++;
if( ch!='%' ) {
if(isspace(ch)) {
/* match any number of whitespace */
while(isspace(*s)) s++;
} else {
/* match exactly the character ch */
if( *s!=ch ) goto finish;
s++;
}
} else {
/* we have got a '%' */
ch=*f++;
if( ch=='%' ) {
/* match exactly % */
if( *s!=ch ) goto finish;
s++;
} else {
f2=onefmt;
*f2++='%';
*f2++=ch;
ignore=0;
if( ch=='*' ) {
ignore=1;
ch=f2[-1]=*f++;
}
while( isdigit(ch) ) {
ch=*f2++=*f++;
}
if( ch=='l' || ch=='L' || ch=='h' ) {
ch=*f2++=*f++;
}
switch(ch) {
case '[':
while( ch && ch!=']' ) {
ch=*f2++=*f++;
}
if( !ch ) goto error;
break;
case 'e':
case 'f':
case 'g':
case 'd':
case 'o':
case 'i':
case 'u':
case 'x':
case 'c':
case 's':
case 'p':
case 'n': /* special case handled below */
break;
default:
goto error;
}
if( ch!='n' ) {
strcpy(f2,"%n");
if( ignore ) {
p=buffer;
} else {
p=va_arg(args,void *);
}
switch( sscanf( s, onefmt, p, &pos ) ) {
case EOF: goto error;
case 0 : goto finish;
}
if( !ignore ) cnt++;
s+=pos;
} else {
if( !ignore ) {
ip=va_arg(args,int *);
*ip=(int)(s-string);
}
}
}
}
}
if( !*f ) goto finish;
error:
cnt=EOF;
finish:
va_end(args);
return cnt;
}
/* use the substitute now. I know this is dirty trick, but it works. */
#define sscanf purec_sscanf
#endif /* __PUREC__ */
/*}}}*/
/*{{{ int df_open(max_using)*/
int df_open(max_using)
int max_using;
/* open file, parsing using/thru/index stuff
* return number of using specs [well, we have to return something !]
*/
{
static char filename[MAX_LINE_LEN+1]="";
int i;
int name_token;
/*{{{ close file if necessary*/
if (data_fp)
df_close();
/*}}}*/
/*{{{ initialise static variables*/
df_format[0] = '\0'; /* no format string */
df_no_use_specs = 0;
for (i=0; i<NCOL; ++i)
{ use_spec[i].column=i+1; /* default column */
use_spec[i].at = NULL; /* no expression */
}
if (max_using > NCOL)
max_using = NCOL;
df_datum = 0;
df_line_number=0;
df_lower_index = 0;
df_index_step = 1;
df_upper_index = MAXINT;
df_current_index=0;
blank_count = 2;
/* by initialising blank_count, leading blanks will be ignored */
everypoint = everyline=1; /* unless there is an every spec */
firstpoint=firstline=0;
lastpoint=lastline=MAXINT;
df_eof=0;
memset(df_timecol, 0, sizeof(df_timecol));
df_binary=1;
/*}}}*/
assert(max_using <= NCOL);
/* empty name means re-use last one */
{ char name[MAX_LINE_LEN+1];
quote_str(name, c_token, MAX_LINE_LEN);
if (name[0])
strcpy(filename, name);
else if (!filename[0])
int_error("No previous filename", c_token);
}
name_token = c_token++;
/* defer opening until we have parsed the modifiers... */
/*{{{ look for binary*/
if (almost_equals(c_token, "bin$ary"))
{
++c_token;
df_binary=TRUE;
}
else
df_binary=FALSE;
/*}}}*/
/*{{{ deal with index*/
if (almost_equals(c_token, "i$ndex")) {
struct value a;
if (df_binary)
int_error("Binary file format does not allow more than one surface per file",c_token);
++c_token;
df_lower_index = (int)real(const_express(&a));
if (equals(c_token, ":")) {
++c_token;
df_upper_index = (int)magnitude(const_express(&a));
if (df_upper_index < df_lower_index)
int_error("Upper index should be bigger than lower index", c_token);
if (equals(c_token, ":")) {
++c_token;
df_index_step = (int)magnitude(const_express(&a));
if (df_index_step < 1)
int_error("Index step must be positive", c_token);
}
}
else
df_upper_index = df_lower_index;
}
/*}}}*/
/*{{{ deal with every*/
if (almost_equals(c_token, "ev$ery")) {
struct value a;
/* allow empty fields - every a:b:c::e
* we have already established the defaults
*/
if (!equals(++c_token, ":")) {
everypoint = (int)real(const_express(&a));
if (everypoint<1)
int_error("Expected positive integer", c_token);
}
/* if it fails on first test, no more tests will succeed. If it
* fails on second test, next test will succeed with correct c_token
*/
if (equals(c_token, ":") && !equals(++c_token, ":")) {
everyline = (int)real(const_express(&a));
if (everyline<1)
int_error("Expected positive integer", c_token);
}
if (equals(c_token, ":") && !equals(++c_token, ":")) {
firstpoint = (int)real(const_express(&a));
if (firstpoint<0)
int_error("Expected non-negative integer", c_token);
}
if (equals(c_token, ":") && !equals(++c_token, ":")) {
firstline = (int)real(const_express(&a));
if (firstline<0)
int_error("Expected non-negative integer", c_token);
}
if (equals(c_token, ":") && !equals(++c_token, ":")) {
lastpoint = (int)real(const_express(&a));
if (lastpoint<firstpoint)
int_error("Last point must not be before first point", c_token);
}
if (equals(c_token, ":")) {
++c_token;
lastline = (int)real(const_express(&a));
if (lastline<firstline)
int_error("Last line must not be before first line", c_token);
}
}
/*}}}*/
/*{{{ deal with thru*/
/* jev -- support for passing data from file thru user function */
if (almost_equals(c_token, "thru$")) {
c_token++;
if (ydata_func.at)
free(ydata_func.at);
strcpy(c_dummy_var[0], dummy_var[0]);
/* allow y also as a dummy variable.
* during plot, c_dummy_var[0] and [1] are 'sacred'
* ie may be set by splot [u=1:2] [v=1:2], and these
* names are stored only in c_dummy_var[]
* so choose dummy var 2 - can anything vital be here ?
*/
dummy_func = &ydata_func;
strcpy(c_dummy_var[2], "y");
ydata_func.at = perm_at();
dummy_func = NULL;
} else {
if (ydata_func.at)
free(ydata_func.at);
ydata_func.at = NULL;
}
/*}}}*/
/*{{{ deal with using*/
if (almost_equals(c_token,"u$sing"))
{
if (!END_OF_COMMAND && !isstring(++c_token))
{
struct value a;
do /* must be at least one */
{
if (df_no_use_specs >= max_using)
int_error("Too many columns in using specification", c_token);
if (equals(c_token, "("))
{
dummy_func=NULL; /* no dummy variables active */
use_spec[df_no_use_specs++].at = perm_at(); /* it will match ()'s */
}
else
use_spec[df_no_use_specs++].column =
(int)magnitude(const_express(&a));
} while (equals(c_token,":") && ++c_token);
}
if (!END_OF_COMMAND && isstring(c_token)) {
if (df_binary)
int_error("Format string meaningless with binary data",NO_CARET);
quote_str(df_format, c_token, MAX_LINE_LEN);
if (!valid_format( df_format))
int_error( "Please use a double conversion %lf", c_token);
c_token++; /* skip format */
}
}
/*}}}*/
/*{{{ more variable inits*/
point_count = -1; /* we preincrement */
line_count = 0;
/* here so it's not done for every line in df_readline */
if (max_line_len < 160)
line = (char *)alloc(max_line_len=160, "datafile line buffer");
/*}}}*/
/*{{{ open file*/
#if defined(unix) || defined(PIPES)
if (*filename == '<') {
if ((data_fp = popen(filename + 1, "r")) == (FILE *) NULL)
os_error("cannot create pipe for data", name_token);
else
pipe_open = TRUE;
} else
#endif /* unix || PIPES */
if (*filename == '-'){
data_fp=lf_top();
if(!data_fp) data_fp=stdin;
mixed_data_fp=TRUE; /* don't close command file */
} else
if ((data_fp = fopen(filename, "r")) == (FILE *) NULL)
os_error("can't open data file", name_token);
/*}}}*/
return df_no_use_specs;
}
/*}}}*/
/*{{{ void df_close()*/
void df_close()
{
int i;
df_no_cols = 0; /* paranoid - mark $n and column(n) as invalid */
if (!data_fp)
return;
if (ydata_func.at) {
free(ydata_func.at);
ydata_func.at = NULL;
}
/*{{{ free any use expression storage*/
for (i=0; i<df_no_use_specs; ++i)
if (use_spec[i].at)
{
free(use_spec[i].at);
use_spec[i].at = NULL;
}
/*}}}*/
if(!mixed_data_fp) {
#if defined(unix) || defined(PIPES)
if (pipe_open) {
(void) pclose(data_fp);
pipe_open = FALSE;
} else
#endif /* unix || PIPES */
(void) fclose(data_fp);
}
mixed_data_fp=FALSE;
data_fp=NULL;
}
/*}}}*/
/*{{{ int df_readline(v, max)*/
/* do the hard work... read lines from file,
* - use blanks to get index number
* - ignore lines outside range of indices required
* - fill v[] based on using spec if given
*/
int df_readline(v, max)
double v[];
int max;
{
assert(data_fp != NULL);
assert(!df_binary);
assert(max <= NCOL);
assert(max_line_len); /* alloc-ed in df_open() */
/* catch attempt to read past EOF on mixed-input */
if (df_eof)
return DF_EOF;
while (fgets(line, max_line_len-1, data_fp) != (char *) NULL)
/*{{{ process line*/
{
char *s;
int line_okay = 1;
int output=0; /* how many numbers written to v[] */
/*{{{ if line was longer than our buffer, realloc and read some more*/
{
int len=strlen(line);
while (line[len-1] != '\n') {
/* buffer we provided may not be full - dont grab extra
* memory un-necessarily. This may trap a problem with last
* line in file not being properly terminated - each time
* through a replot loop, it was doubling buffer size
*/
if ( (max_line_len-len) < 32)
line = ralloc(line, max_line_len*=2, "datafile line buffer");
if (!fgets(line+len-1, max_line_len - len, data_fp))
break; /* unexpected end of file, but we have something to do */
/* get length of just the new bit, rather than whole lot */
len += strlen(line+len);
}
}
/*}}}*/
s=line;
++df_line_number;
++df_datum;
df_no_cols = 0;
/*{{{ check for blank lines, and reject by index/every*/
/*{{{ skip leading spaces*/
while (isspace(*s))
++s; /* will skip the \n too, to point at \0 */
/*}}}*/
/*{{{ skip comments*/
if (is_comment(*s))
continue; /* ignore comments */
/*}}}*/
/*{{{ check EOF on mixed data*/
if (mixed_data_fp && is_EOF(*s))
{
df_eof=1; /* trap attempts to read past EOF */
return DF_EOF;
}
/*}}}*/
if (*s==0)
/*{{{ its a blank line - update counters and continue or return*/
{
/* argh - this is complicated ! we need to
* ignore it if we haven't reached first index
* report EOF if passed last index
* report blank line unless we've already done 2 blank lines
*
* - I have probably missed some obvious way of doing all this,
* but its getting late
*/
point_count=-1; /* restart counter within line */
if (++blank_count==1) {
/* first blank line */
++line_count;
}
if (blank_count == 2)
{ /* just reached end of a group/surface */
++df_current_index;
line_count = 0;
df_datum = 0;
/* ignore line if current_index has just become
* first required one - client doesn't want this
* blank line. While we're here, check for <=
* - we need to do it outside this conditional, but
* probably no extra cost at assembler level
*/
if (df_current_index <= df_lower_index)
continue; /* dont tell client */
/* df_upper_index is MAXINT-1 if we are not doing index */
if (df_current_index > df_upper_index)
{
/* oops - need to gobble rest of input if mixed */
if (mixed_data_fp)
continue;
else
{
df_eof=1;
return DF_EOF; /* no point continuing */
}
}
}
/* dont tell client if we haven't reached first index */
if (df_current_index < df_lower_index)
continue;
/* ignore blank lines after blank_index */
if (blank_count > 2)
continue;
return DF_FIRST_BLANK - (blank_count-1);
}
/*}}}*/
/* get here => was not blank */
blank_count = 0;
/*{{{ ignore points outside range of index*/
/* we try to return end-of-file as soon as we pass upper index,
* but for mixed input stream, we must skip garbage
*/
if (df_current_index < df_lower_index ||
df_current_index > df_upper_index ||
((df_current_index - df_lower_index)%df_index_step) != 0)
continue;
/*}}}*/
/*{{{ reject points by every*/
/* accept only lines with (line_count%everyline)==0 */
if (line_count<firstline || line_count > lastline ||
(line_count-firstline)%everyline != 0
)
continue;
/* update point_count. ignore point if point_count%everypoint != 0 */
if (++point_count < firstpoint || point_count > lastpoint ||
(point_count-firstpoint)%everypoint != 0
)
continue;
/*}}}*/
/*}}}*/
if (*df_format)
/*{{{ do a sscanf*/
{
int i;
assert(NCOL==7);
/* check we have room for at least 7 columns */
if (df_max_cols < 7)
df_column = (df_column_struct *)ralloc(df_column, (df_max_cols=7)*sizeof(df_column_struct), "datafile columns");
df_no_cols = sscanf(line, df_format,
&df_column[0].datum,
&df_column[1].datum,
&df_column[2].datum,
&df_column[3].datum,
&df_column[4].datum,
&df_column[5].datum,
&df_column[6].datum);
if (df_no_cols == EOF)
{
df_eof=1;
return DF_EOF; /* tell client */
}
for (i=0; i < df_no_cols; ++i ) /* may be zero */
{ df_column[i].good=1;
df_column[i].position=NULL; /* cant get a time */
}
}
/*}}}*/
else
/*{{{ read the data column by column*/
{
/* implement our own sscanf that skips lines with invalid
* data if a using statement was given
* convert the array to its constituents
*/
df_no_cols = 0;
while (*s)
{
int used;
/* check store - double max cols or add 20, whichever is greater */
if (df_max_cols <= df_no_cols)
df_column=(df_column_struct *)ralloc(df_column, (df_max_cols += (df_max_cols < 20 ? 20 : df_max_cols))*sizeof(df_column_struct), "datafile column");
/* have always skipped spaces at this point */
df_column[df_no_cols].position = s;
if (check_missing(s))
df_column[df_no_cols].good = -1;
else {
/* cannot trust strtod - eg strtod("-",&p) */
df_column[df_no_cols].good = sscanf(s, "%lf%n", &df_column[df_no_cols].datum, &used);
/* it might be a fortran double or quad precision. 'used'
* is only safe if good is 1
*/
if (df_column[df_no_cols].good &&
(s[used]=='d' || s[used]=='D' || s[used]=='q' || s[used]=='Q')
) {
/* might be fortran double */
s[used]='e';
/* and try again */
df_column[df_no_cols].good = sscanf(s, "%lf", &df_column[df_no_cols].datum);
}
}
++df_no_cols;
/*{{{ skip chars to end of column*/
while ((!isspace(*s)) && (*s != '\0'))
++s;
/*}}}*/
/*{{{ skip spaces to start of next column*/
while (isspace(*s))
++s;
/*}}}*/
}
}
/*}}}*/
/*{{{ copy column[] to v[] via use[]*/
{
int limit=(df_no_use_specs ? df_no_use_specs : NCOL);
if (limit>max)
limit=max;
for (output=0; output<limit; ++output)
{
/* if there was no using spec, column is output+1 and at=NULL */
int column=use_spec[output].column;
if (use_spec[output].at)
{ struct value a;
/* no dummy values to set up prior to... */
evaluate_at(use_spec[output].at, &a);
if (undefined)
return DF_UNDEFINED; /* store undefined point in plot */
v[output]=real(&a);
}
else if (df_timecol[output])
{ struct tm tm;
if (column >= df_no_cols ||
df_column[column-1].good == -1 /* missing */ ||
!df_column[column-1].position ||
!gstrptime(df_column[column-1].position,timefmt,&tm)
)
{
/* line bad only if user explicitly asked for this column */
if (df_no_use_specs)
line_okay=0;
/* return or ignore line depending on line_okay */
break;
}
v[output] = (double) gtimegm(&tm);
}
else if (column)
{
v[output] = df_column[column-1].datum;
if ( (column > df_no_cols) || df_column[column-1].good!=1)
{
/* line bad only if user explicitly asked for this column */
if (df_no_use_specs)
line_okay=0;
break; /* return or ignore depending on line_okay */
}
}
else
v[output] = df_datum; /* using 0 */
}
}
/*}}}*/
if (!line_okay)
continue;
/* output == df_no_use_specs if using was specified
* - actually, smaller of df_no_use_specs and max
*/
assert (df_no_use_specs==0 || output==df_no_use_specs || output==max);
return output;
}
/*}}}*/
/* get here => fgets failed */
df_no_cols = 0; /* no longer needed - mark column(x) as invalid */
df_eof=1;
return DF_EOF;
}
/*}}}*/
/*{{{ int df_2dbinary(this_plot)*/
int df_2dbinary(this_plot)
struct curve_points *this_plot;
{
int_error("Binary file format for 2d data not yet defined", NO_CARET);
return 0; /* keep compiler happy */
}
/*}}}*/
/*{{{ int df_3dbinary(this_plot, ret_this_iso)*/
/*
* formerly in gnubin.c
*
* modified by div for 3.6
* obey the 'every' field from df_open
* outrange points are marked as such, not omitted
* obey using - treat x as column 1, y as col 2 and z as col 3
* ( ie $1 gets x, $2 gets y, $3 gets z)
*
* we are less optimal for case of log plot and no using spec,
* (call log too often) but that is price for flexibility
* I suspect it didn't do autoscaling of x and y for log scale
* properly ?
*
* Trouble figuring out file format ! Is it
width x1 x2 x3 x4 x5 ...
y1 z11 z12 z13 z14 z15 ...
y2 x21 z22 z23 .....
. .
. .
. .
* with perhaps x and y swapped...
*
* - presumably rows continue to end of file, hence no indexing...
*
* Last update: 3/3/92 for Gnuplot 3.24.
* Created from code for written by RKC for gnuplot 2.0b.
*
* 19 September 1992 Lawrence Crowl (crowl@cs.orst.edu)
* Added user-specified bases for log scaling.
*
* Copyright (c) 1991,1992 Robert K. Cunningham, MIT Lincoln Laboratory
*
*/
/*
Here we keep putting new plots onto the end of the linked list
We assume the data's x,y values have x1<x2, x2<x3... and
y1<y2, y2<y3... .
Actually, I think the assumption is less stron than that--it looks like
the direction just has to be the same.
This routine expects the following to be properly initialized:
is_log_x, is_log_y, and is_log_z
base_log_x, base_log_y, and base_log_z
log_base_log_x, log_base_log_y, and log_base_log_z
xmin,ymin, and zmin
xmax,ymax, and zmax
autoscale_lx, autoscale_ly, and autoscale_lz
does the autoscaling into the array versions (min_array[], max_array[])
*/
int
df_3dbinary(this_plot)
struct surface_points *this_plot;
{
float GPFAR * GPFAR *matrix, GPFAR *rt, GPFAR *ct;
int nr,nc;
int width,height;
int row,col;
struct iso_curve *this_iso;
double used[3]; /* output from using manip */
struct coordinate *point;
assert(df_binary);
if (df_eof)
return 0; /* hope caller understands this */
if(!fread_matrix(data_fp,&matrix,&nr,&nc,&rt,&ct))
int_error("Binary file read error: format unknown!",NO_CARET);
if (nc==0 || nr==0)
int_error("Read grid of zero height or zero width", NO_CARET);
/* fread_matrix() drains the file */
df_eof=1;
this_plot->plot_type = DATA3D;
this_plot->has_grid_topology = TRUE;
if (df_no_use_specs != 0 && df_no_use_specs != 3)
int_error("Current implementation requires full using spec",NO_CARET);
if (df_max_cols < 3 &&
!(df_column = (df_column_struct *)ralloc(df_column, (df_max_cols=3)*sizeof(df_column_struct), "datafile columns"))
)
int_error("Out of store in binary read", c_token);
df_no_cols=3;
df_column[0].good=df_column[1].good=df_column[2].good=1;
assert(everyline > 0);
assert(everypoint > 0);
width=(nc-firstpoint+everypoint-1)/everypoint; /* ? ? ? ? ? */
height=(nr-firstline+everyline-1)/everyline; /* ? ? ? ? ? */
for(row=firstline; row < nr; row+=everyline){
df_column[1].datum=rt[row];
this_iso = iso_alloc(width);/*Allocate the correct number of entries*/
point=this_iso->points;
for(col = firstpoint; col< nc; col+=everypoint, ++point){/* Cycle through data */
/*{{{ process one row*/
int i;
df_column[0].datum=ct[col];
df_column[2].datum=matrix[row][col];
/*{{{ pass through using spec*/
for (i=0; i<3; ++i)
{
int column=use_spec[i].column;
if (df_no_use_specs==0)
used[i]=df_column[i].datum;
else if (use_spec[i].at)
{
struct value a;
evaluate_at(use_spec[i].at, &a);
if (undefined)
{
point->type=UNDEFINED;
goto skip; /* continue _outer_ loop */
}
used[i]=real(&a);
}
else if (column < 1 || column > df_no_cols)
{
point->type=UNDEFINED;
goto skip;
}
else
used[i]=df_column[column-1].datum;
}
/*}}}*/
point->type=INRANGE; /* so far */
/*{{{ autoscaling/clipping*/
/*{{{ autoscale/range-check x*/
if (used[0]>0 || !is_log_x)
{
if (used[0] < min_array[FIRST_X_AXIS])
{ if (autoscale_lx & 1)
min_array[FIRST_X_AXIS] = used[0];
else
point->type=OUTRANGE;
}
if (used[0] > max_array[FIRST_X_AXIS])
{ if (autoscale_lx & 2)
max_array[FIRST_X_AXIS] = used[0];
else
point->type=OUTRANGE;
}
}
/*}}}*/
/*{{{ autoscale/range-check y*/
if (used[1]>0 || !is_log_y)
{
if (used[0] < min_array[FIRST_Y_AXIS])
{ if (autoscale_ly & 1)
min_array[FIRST_Y_AXIS] = used[1];
else
point->type=OUTRANGE;
}
if (used[1] > max_array[FIRST_Y_AXIS])
{ if (autoscale_ly & 2)
max_array[FIRST_Y_AXIS] = used[0];
else
point->type=OUTRANGE;
}
}
/*}}}*/
/*{{{ autoscale/range-check z*/
if (used[2]>0 || !is_log_z)
{
if (used[2] < min_array[FIRST_Z_AXIS])
{ if (autoscale_lz & 1)
min_array[FIRST_Z_AXIS] = used[2];
else
point->type=OUTRANGE;
}
if (used[2] > max_array[FIRST_Z_AXIS])
{ if (autoscale_lz & 2)
max_array[FIRST_Z_AXIS] = used[2];
else
point->type=OUTRANGE;
}
}
/*}}}*/
/*}}}*/
/*{{{ log x*/
if (is_log_x)
{
if (used[0] < 0.0)
{
point->type = UNDEFINED;
goto skip;
}
else if (used[0] == 0.0)
{
point->type = OUTRANGE;
used[0]=-VERYLARGE;
}
else
used[0] = log(used[0])/log_base_log_x;
}
/*}}}*/
/*{{{ log y*/
if (is_log_y)
{
if (used[1] < 0.0)
{
point->type = UNDEFINED;
goto skip;
}
else if (used[1] == 0.0)
{
point->type=OUTRANGE;
used[1]=-VERYLARGE;
}
else
used[1] = log(used[1])/log_base_log_y;
}
/*}}}*/
/*{{{ log z*/
if (is_log_z)
{
if (used[2] < 0.0)
{
point->type = UNDEFINED;
goto skip;
}
else if (used[2]==0.0)
{
point->type = OUTRANGE;
used[2]=-VERYLARGE;
}
else
used[2] = log(used[2])/log_base_log_z;
}
/*}}}*/
point->x = used[0];
point->y = used[1];
point->z = used[2];
/* some of you wont like this, but I say goto is for this */
skip:
; /* ansi requires this */
/*}}}*/
}
this_iso->p_count = width;
this_iso->next = this_plot->iso_crvs;
this_plot->iso_crvs = this_iso;
this_plot->num_iso_read++;
}
free_matrix(matrix,0,nr-1,0,nc-1);
free_vector(rt,0,nr-1);
free_vector(ct,0,nc-1);
return(nc);
}
/*}}}*/
/* stuff for implementing the call-backs for picking up data values
* do it here so we can make the variables private to this file
*/
/*{{{ void f_dollars(x)*/
void f_dollars(x)
union argument *x;
{
int column = x->v_arg.v.int_val - 1;
/* we checked it was an integer >= 0 at compile time */
struct value a;
if (column==-1)
{
push ( Gcomplex(&a, (double)df_datum, 0.0)); /* $0 */
}
else if (column >= df_no_cols || !df_column[column].good)
{ undefined = TRUE;
push (&(x->v_arg)); /* this okay ? */
}
else
push( Gcomplex(&a, df_column[column].datum, 0.0) );
}
/*}}}*/
/*{{{ void f_column()*/
void f_column()
{
struct value a;
int column;
(void) pop(&a);
column = (int) magnitude(&a) - 1;
if (column < 0 || column >= df_no_cols || !df_column[column].good)
{ undefined = TRUE;
push (&a); /* any objection to this ? */
}
else
push( Gcomplex(&a, df_column[column].datum, 0.0) );
}
/*}}}*/
/*{{{ void f_valid()*/
void f_valid()
{
struct value a;
int column,good;
(void) pop(&a);
column = (int) magnitude(&a) - 1;
good = column >= 0 && column < df_no_cols && df_column[column].good;
push (Ginteger(&a, good));
}
/*}}}*/
/* count columns in timefmt */
/*{{{ static int get_time_cols(fmt)*/
static int get_time_cols(fmt)
char *fmt; /* format string */
{
int cnt,i;
char *p;
p = fmt;
cnt = 0;
while ( isspace(*p) )
p++;
if ( ! strlen(p))
int_error("Empty time-data format",NO_CARET);
cnt ++;
for(i=0;i<strlen(p)-1;i++) {
if ( isspace(p[i]) && !isspace(p[i+1]) )
cnt++;
}
return(cnt);
}
/*}}}*/
/* modify default use_spec, applies for no user spec and time datacolumns */
/*{{{ static void mod_def_usespec(specno,jump)*/
static void mod_def_usespec(specno,jump)
int specno; /* which spec in ?:?:? */
int jump; /* no of columns in timefmt (time data) */
{
int i;
for (i=specno+1; i<NCOL; ++i)
use_spec[i].column += jump; /* add no of columns in time to the rest */
df_no_use_specs = 0;
}
/*}}}*/
/*{{{ static int check_missing(s)*/
static int check_missing(s)
char *s;
{
if ( missing_val != NULL ) {
if ( !strncmp(s,missing_val,strlen(missing_val)) && isspace(s[strlen(missing_val)])) {
return (1);; /* store undefined point in plot */
}
}
return(0);
}
/*}}}*/